home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
007
/
ibmbios.aqm
/
ibmbios.asm
Wrap
Assembly Source File
|
1984-10-14
|
43KB
|
1,189 lines
PAGE 60,132
; Title: IBM-DOS Basic Input/Output System
; Name: IBMBIO.COM
; Purpose: Hardware dependent routines that form the Basic
; Input/Output System (BIOS) for MS-DOS on the IBM
; Personal Computer.
; Revision: January 1, 1983 2:00 PM
DATA SEGMENT AT 00H
ORG 006CH
KYBD_BRK_VECTOR DW ?
ORG 400H
RS232_BASE DW 4 DUP (?) ; Addresses of RS-232 Adapters
PRINTER_BASE DW 4 DUP (?) ; Addresses of printers
EQUIP_FLAG DW ? ; Installed hardware
MFG_TST DB ? ; Initialization Flag
MEMORY_SIZE DW ? ; Memory size in K bytes
IO_RAM_SIZE DW ? ; Memory in I/O channel
; ----- Keyboard Data Areas
KB_FLAG DB ? ; Shift state bits
KB_FLAG_1 DB ? ; Second byte of keyboard status
ALT_INPUT DB ? ; Storage for alternate keypad entry
BUFFER_HEAD DW ? ; Pointer to head of keyboard buffer
BUFFER_TAIL DW ? ; Pointer to tail of keyboard buffer
KB_BUFFER DW 16 DUP (?) ; Room for 15 entries
KB_BUFFER_END LABEL WORD
ORG 500H
STATUS_BYTE DB ? ; Set to 0 by INIT
DB 3 DUP (?)
ONE_DRIVE_IO DB ? ; Used in single drive copies
DATA ENDS
DOSSEG SEGMENT AT 0BFH
DOSSEG_FAR LABEL FAR
DOSSEG ENDS
CODE SEGMENT PUBLIC 'CODE'
ASSUME CS:CODE,SS:CODE,ES:DATA,DS:DATA
BIOS:
; The following jump table provides MS-DOS with access to the
; respective hardware dependent driver routines.
JMP INIT ; System initialization
JMP STATUS ; Console Status Check
JMP CONIN ; Console Input
JMP CONOUT ; Console Output
JMP PRINT ; Printer Output
JMP AUXIN ; Auxiliary Input
JMP AUXOUT ; Auxiliary Output
JMP READ ; Disk Read
JMP WRITE ; Disk Write
JMP DSKCHG ; Return Disk Change Status
JMP SETDATE ; Set Current Date
JMP SETTIME ; Set Current Time
JMP GETDATE ; Read Time and Date
JMP FLUSH ; Flush Keyboard Input Buffer
JMP MAPDEV ; Device mapping
PAPER_OUT_MSG:
DB 13,10,'Out of paper',13,10,00
PRINTER_FAULT_MSG:
DB 13,10,'Printer fault',13,10,00
AUX_ERROR_MSG:
DB 13,10,'Aux I/O error',13,10,00
; Procedure: STATUS
; Purpose: Return status of console input
; Entry: No significant registers
; Exit: If char is ready:
; Zero Flag is cleared
; [AL] = Character
; If no char is ready:
; Zero Flag is set
; Once character is returned with this call, the same character is
; returned every time the call is made until a CONIN call is made.
; In other words, this call leaves the character in the input buffer,
; and only CONIN can remove it.
; No registers other than [AL] are (or may be) changed.
STATUS_FAR PROC FAR
STATUS LABEL NEAR
MOV AL,CS:BYTE PTR CHAR_BUFFER ; Check for a character
OR AL,AL ; Character available ?
JNZ S2 ; Yes, exit - character in [AL] Zero Flag set
PUSH DX ; Save [DX] on stack for later restoration
XCHG DX,AX ; Save [AX] in [DX]
MOV AH,01 ; Prep for Keyboard I/O
INT 16H ; (See page A-23 of IBM Tech Ref Manual)
JZ S1 ; Zero Flag indicates char is available
CMP AX,7200H ; Do we need to replace char from I/O handler ?
JNZ S1 ; No, continue
MOV AL,10H ; Yes, substitute a Control-P
OR AL,AL ; Set flags for Control-P
S1:
MOV AH,DH ; Restore [AH] from prior save to [DH]
POP DX ; Restore [DX] off stack
S2:
RET
STATUS_FAR ENDP
; Procedure: KEYBRK
; Purpose: New Keyboard Break Code handler. INIT points
; Get Control on Keyboard Break (1BH) to this code.
; Entry: On INT 1BH
; Exit: CHAR_BUFFER = 03H
KEYBRK PROC NEAR
MOV CS:BYTE PTR CHAR_BUFFER,03
NOP
IRET_INSTRUCTION:
IRET
KEYBRK ENDP
; Procedure: CONIN
; Purpose: Wait for a character from the console, then return the
; character in [AL].
; Entry: No significant registers
; Exit: [AL] = Character
CONIN_FAR PROC FAR
CI1:
XCHG DX,AX ; Restore [AX] from prior save to [DX]
POP DX ; Restore [DX] off stack, drop thru to CONIN
CONIN LABEL NEAR
MOV AL,00 ; Prep for exchange with char buffer
XCHG AL,CS:BYTE PTR CHAR_BUFFER ; Get char from buffer and clear buffer
OR AL,AL ; Char in buffer ?
JNZ CI4 ; Yes, return to caller with char in AL
PUSH DX ; Save [DX] for later restore
XCHG DX,AX ; Save [AX] in [DX] for later [AH] restore
MOV AH,00 ; Prep for keyboard I/O
INT 16H ; Read next ASCII char struck from keyboard
OR AX,AX ; Error in reading keyboard ?
JZ CI1 ; If no char or scan code, try again
CMP AX,7200H ; Char need replacement ?
JNZ CI2 ; No, skip char replacement
MOV AL,10H ; Replace with Control-P
CI2:
CMP AL,00 ; NUL in [AL] ?
JNZ CI3 ; No, restore blown registers and exit
; Yes, put scan code in Char Buffer and
MOV CS:BYTE PTR CHAR_BUFFER,AH ; indicate system or application program
CI3: ; should examine a second code for function
MOV AH,DH ; Restore [AH] from prior save to [DH]
POP DX ; Restore [DX] off stack
CI4:
RET
CONIN_FAR ENDP
; Procedure: CONOUT
; Purpose: Output the char in [AL] to the console
; Entry: [AL] = char
; Exit: No registers may be affected.
CONOUT_FAR PROC FAR
CONOUT LABEL NEAR
PUSH BP ; Save registers on the stack
PUSH AX
PUSH BX
PUSH SI
PUSH DI
MOV AH,0EH ; Prep for ROM BIOS Call - Write Teletype
MOV BX,0007H ; -set foreground color and alpha mode
INT 10H ; Call Video I/O
POP DI ; Restore registers off the stack
POP SI
POP BX
POP AX
POP BP
RET
CONOUT_FAR ENDP
; Procedure: PRINT
; Purpose: Output the Char in [AL] to the printer
; Entry: [AL] = Char
; Exit: No registers may be affected.
PRINT_FAR PROC FAR
PRINT LABEL NEAR
PUSH AX ; Save registers on stack
PUSH DX
MOV CS:BYTE PTR PRINTER_ERROR,00
NOP
P1:
MOV DX,0000 ; Prep for INT, indicate Printer 0
MOV AH,00 ; Request - print char in [AL]
INT 17H ; Printer I/O
MOV DX,OFFSET PAPER_OUT_MSG ; Default to Paper Out Message
TEST AH,20H ; Out of paper ?
JNZ P2 ; Yes, output error message
MOV DX,OFFSET PRINTER_FAULT_MSG ; Default to Printer Fault Message
TEST AH,05H ; Printer Fault ?
JZ P3 ; No, continue
XOR CS:BYTE PTR PRINTER_ERROR,01
NOP
JNZ P1
P2:
CALL PRINT_STRING ; Output printer error message in DX to console
P3:
POP DX ; Restore registers off stack
POP AX
RET
PRINT_FAR ENDP
PRINT_STRING PROC NEAR
XCHG SI,DX ; [DX] point to string to print
PS1: ; MSG referred to in LODS is dummy
LODS CS:BYTE PTR PAPER_OUT_MSG ; Get byte to output to console
AND AL,7FH ; Clear high bit
JZ PS2 ; Exit if we're through
CALL CONOUT_FAR ; Output byte we got from message string
JMP PS1 ; Loop until entire message string output
PS2:
XCHG SI,DX ; Restore registers to value on entry
RET
PRINT_STRING ENDP
; Procedure: AUXIN
; Purpose: Wait for a byte from the auxiliary input device, then
; return with the byte in [AL]
; Entry: No significant registers.
; Exit: [AL] = Char, no other registers may be affected
AUXIN_FAR PROC FAR
AUXIN LABEL NEAR
PUSH DX ; Save registers on stack
PUSH AX
MOV DX,0000 ; Prep for RS-232 I/O Interrupt 14H
MOV AH,02 ; Receive a byte from Card 0
INT 14H
MOV DX,OFFSET AUX_ERROR_MSG ; Default to error message
TEST AH,0EH ; Any errors ?
JZ AI1 ; No, do normal exit
CALL PRINT_STRING ; Yes, output string pointed to by DX
AI1:
POP DX ; Restore registers
MOV AH,DH
POP DX
RET
AUXIN_FAR ENDP
; Procedure: AUXOUT
; Purpose: Output the byte in [AL] to the auxiliary output device.
; Entry: [AL] = char
; Exit: No registers may be affected
AUXOUT_FAR PROC FAR
AUXOUT LABEL NEAR
PUSH AX ; Save registers on stack
PUSH DX
MOV AH,01 ; Prep for RS-232 I/O Interrupt 14H
MOV DX,0000 ; Send char in [AL] out Card 0
INT 14H
TEST AH,80H ; Operation successful ?
JZ P3 ; Yes, do normal exit using code in PRINT
MOV DX,OFFSET AUX_ERROR_MSG ; No, point to error message
JMP P2 ; Use error exit in AUXIN
AUXOUT_FAR ENDP
; Procedure: DSKCHG
; Purpose: Called whenever a directory search has been made and
; disk could have legally been changed to minimize
; unnecessary re-reading of disk directory information
; if the disk has not been changed, and to provide
; configuration information if it has.
; Entry: [AL] = Disk Drive #
; [AH] = 0
; Exit: [AH] = -1 if disk has been changed
; 0 if it is not known whether disk has been changed
; 1 if disk could not have been changed
; [AL] = I/O driver # to use for this diskette and drive
; Carry Flag = Clear
; If DSKCHG requires a disk read and the read fails:
; Carry Flag = Set
; [AL] = Error code (See READ or WRITE)
DSKCHG_FAR PROC FAR
DSKCHG LABEL NEAR
SHL AL,1 ; Only 2 drivers per drive supported
RET
DSKCHG_FAR ENDP
; Procedure: SETDATE
; Purpose: Set system calender
; Entry: [AX] = count of days since January 1, 1980
; Exit: No significant registers
SETDATE_FAR PROC FAR
SETDATE LABEL NEAR
MOV CS:WORD PTR DATE_STORAGE,AX ; Save # of days since Jan 1
XOR AX,AX ; Clear [AX]
INT 1AH ; TIME_OF_DAY Interrupt
RET
SETDATE_FAR ENDP
; Procedure: SETTIME
; Purpose: Set system clock
; Entry: [CX] and [DX] have the current time
; [CH] = hours (0 to 23)
; [CL] = minutes (0 to 59)
; [DH] = seconds (0 to 59)
; [DL] = hundredths of seconds (0 to 99)
; Each of these is a binary number that has been
; checked for proper range.
; Exit: No significant registers
SETTIME_FAR PROC FAR
SETTIME LABEL NEAR
MOV AL,60 ; Prep to convert hours to minutes
MUL CH ; [AX] = [CH]*[AL] = Hours*Minutes
MOV CH,00
ADD AX,CX ; [AX] = ([CH]*[AL])+[CL] = Total Minutes
MOV CX,6000 ; [CX] = # of hundredths of seconds in a minute
MOV BX,DX
MUL CX ; Convert time to hundredths of seconds
MOV CX,AX
MOV AL,100 ; Convert to format required by TIME_OF_DAY
MUL BH
ADD CX,AX
ADC DX,+00
MOV BH,00
ADD CX,BX
ADC DX,00
XCHG DX,AX
XCHG CX,AX
MOV BX,59659
MUL BX
XCHG DX,CX
XCHG DX,AX
MUL BX
ADD AX,CX
ADC DX,00
XCHG DX,AX
MOV BX,0005
DIV BL
MOV CL,AL
MOV CH,00
MOV AL,AH
CBW
XCHG DX,AX
DIV BX
MOV DX,AX
MOV AH,01 ; Count is now in [CX] and [DX]
INT 1AH ; Set Time_of_day
RET
SETTIME_FAR ENDP
; Procedure: GETDATE
; Purpose: Read date and time
; Entry: No significant registers
; Exit: [AX] = count of days since January 1, 1980
; [CH] = hours
; [CL] = minutes
; [DH] = seconds
; [DL] = hundredths of seconds
GETDATE_FAR PROC FAR
GETDATE LABEL NEAR
PUSH BX ; Save [BX] on stack
MOV AX,00 ; Prep to read TIME_OF_DAY
INT 1AH ; Do it
ADD CS:WORD PTR DATE_STORAGE,AX ; Add Current Clock setting to DATE
MOV AX,CX ; Convert TIME_OF_DAY to required format
MOV BX,DX
SHL DX,1
RCL CX,1
SHL DX,1
RCL CX,1
ADD DX,BX
ADC AX,CX
XCHG DX,AX
MOV CX,59659
DIV CX
MOV BX,AX
XOR AX,AX
DIV CX
MOV DX,BX
MOV CX,200
DIV CX
CMP DL,100
JC GD1
SUB DL,100
GD1:
CMC
MOV BL,DL
RCL AX,1
MOV DL,00
RCL DX,1
MOV CX,60
DIV CX
MOV BH,DL
DIV CL
XCHG AL,AH
MOV DX,BX
XCHG CX,AX
MOV AX,CS:WORD PTR DATE_STORAGE
POP BX
RET
GETDATE_FAR ENDP
; Procedure: FLUSH
; Purpose: Flush the keyboard buffer
; Entry: No significant registers
; Exit: Type-ahead buffer is cleared
FLUSH_FAR PROC FAR
FLUSH LABEL NEAR
MOV CS:BYTE PTR CHAR_BUFFER,00 ; Clear char buffer
NOP
PUSH DS ; Save [DS] on stack
XOR BP,BP
MOV DS,BP
MOV BUFFER_HEAD,OFFSET KB_BUFFER-400H
MOV BUFFER_TAIL,OFFSET KB_BUFFER-400H
POP DS ; Restore [DS]
RET
FLUSH_FAR ENDP
; Procedure: MAPDEV
; Purpose: Map physical disk drives with their I/O drivers.
; Entry: [AL] = I/O driver used to read the FAT
; [AH] = First byte of FAT (range F8 to FF)
; Exit: [AL] = I/O driver for this diskette and drive
MAPDEV_FAR PROC FAR
MAPDEV LABEL NEAR
AND AH,01 ; Test first byte of FAT (FF = 1 = 2-sided)
OR AL,AH ; Add result to even I/O driver # in [AL]
RET
MAPDEV_FAR ENDP
; Procedure: MORE_INIT
; Purpose: Continue initialization process started by INIT
; Entry:
; Exit:
MORE_INIT_FAR PROC FAR
MORE_INIT LABEL NEAR
XOR DX,DX ; Clear [AX] and [DX]
XOR AX,AX
MOV SS,AX ; Set stack just below IBMBIO.COM
MOV SP,0600H
INT 13H ; Reset Disk System [AH] = 0
MOV AL,10100011B ; Initialize RS-232_I/O
INT 14H ; 2400 baud, no parity, 8 bits + 1 stop bit
MOV AH,01 ; Initialize PRINTER_I/O
INT 17H
MOV DS,DX ; Clear [DS] and [ES] for Int vector patching
MOV ES,DX
MOV AX,0060H ; Set-up segment register address for vector
MOV WORD PTR KYBD_BRK_VECTOR+2,AX ; Keyboard break vector
MOV WORD PTR KYBD_BRK_VECTOR,OFFSET KEYBRK
MOV DI,0004 ; Patch INT 1 (Single Step) to IRET
MOV BX,OFFSET IRET_INSTRUCTION
XCHG BX,AX
STOSW
XCHG BX,AX
STOSW
ADD DI,+04 ; Patch INT 3 (Break Point) to IRET
XCHG BX,AX
STOSW
XCHG BX,AX
STOSW
XCHG BX,AX ; Patch INT 4 (Overflow) to IRET
STOSW
XCHG BX,AX
STOSW
MOV WORD PTR STATUS_BYTE,DX ; Clear memory location 500H (WHY ???)
MOV AX,0BFH ; Move MS-DOS down to first segment
MOV ES,AX ; above the I/O System
MOV CX,1000H ; Dos length / 2
CLD
MOV AX,0E0H
MOV DS,AX
XOR DI,DI
MOV SI,DI
REPZ MOVSW
PUSH CS ; Set [DS] = [CS]
POP DS
INT 11H ; Determine equipment attached to system
ROL AL,1 ; specifically # of floppy drives
ROL AL,1
AND AX,0003 ; Mask in # of floppy drives
JNZ SHORT MORONE
ASSUME DS:CODE
MOV BYTE PTR ONE_DRIVE,01 ; Indicate only one floppy
ASSUME DS:DATA
INC AX ; Extra increment with only one drive
MORONE:
INC AX ; Prep for # of I/O drivers
SHL AL,1
MOV SI,OFFSET INIT_TABLE
MOV [SI],AL ; Move # of I/O drivers to Init Table
INT 12H ; Determine memory size and pass to MS-DOS
MOV CL,06
SHL AX,CL
XCHG DX,AX
CALL DOSSEG_FAR
STI
XOR AX,AX ; Set up INT 25 & 26 (Direct read and write)
MOV ES,AX
MOV DI,0094H
MOV AX,OFFSET DIRECT_READ
STOSW
MOV AX,CS
STOSW
MOV AX,OFFSET DIRECT_WRITE
STOSW
MOV ES:[DI],CS
MOV DX,100H ; Set up Disk Transfer Address
MOV AH,1AH
INT 21H
MOV CX,WORD PTR DS:6 ; Set up data segment and open file
SUB CX,0100H
MOV BX,DS
PUSH CS
POP DS
MOV DX,OFFSET COMMAND_FCB
MOV AH,0FH
INT 21H
OR AL,AL ; Good open ?
JNZ BAD_OPERATION
ASSUME DS:CODE
MOV WORD PTR RECORD_LENGTH,0001 ; Force record length to 1
MOV AH,27H ; and read file
INT 21H
JCXZ BAD_OPERATION ; Good read ?
CMP AL,01
JNZ BAD_OPERATION
MOV DS,BX ; Make all segment registers the same
MOV ES,BX
MOV SS,BX
MOV SP,005CH ; Set stack to standard value
XOR AX,AX ; Save last DTA for RET L at end of INIT
PUSH AX
MOV DX,0080H ; Set-up new Disk Transfer Address
MOV AH,1AH
INT 21H
PUSH BX
MOV AX,0100H ; Push offset for RET L on stack
PUSH AX
RET
MORE_INIT_FAR ENDP
BAD_OPERATION:
MOV DX,OFFSET BAD_COMMAND_MSG
CALL PRINT_STRING
STALL: JMP STALL ; Hang system
COMMAND_FCB:
DB 1,'COMMAND COM'
DB 0,0
RECORD_LENGTH:
DB 23 DUP (0)
BAD_COMMAND_MSG:
DB 13,10,'Bad or missing Command Interpreter',13,10,00
INIT_TABLE:
DB 4 ; Number of I/O drivers
DB 0 ; Drive 0
DW SDRIVE ; Single density DPT
DB 0 ; Still drive 0
DW DDRIVE ; Double density DPT
DB 1
DW SDRIVE
DB 1
DW DDRIVE
DB 2
DW SDRIVE
DB 2
DW DDRIVE
DB 3
DW SDRIVE
DB 3
DW DDRIVE
DB 0,0,0
SDRIVE:
DW 512 ; SECSIZ - sector size in bytes
DB 1 ; CLUSSIZ - # of sectors in allocation unit
DW 1 ; RESSEC - # of reserved sectors
DB 2 ; FATCNT - # of file allocation tables
DW 64 ; MAXENT - # of directory entries
DW 320 ; DSKSIZ - # of physical sectors
DDRIVE:
DW 512
DB 2
DW 1
DB 2
DW 112
DW 640
DB 168 DUP (0)
DATE_STORAGE:
DW 0
PRINTER_ERROR:
DB 0
CHAR_BUFFER:
DB 0
DB 0
COMMAND_STORAGE:
DB 2
VERIFY_FLAG:
DB 0
IO_DRIVER:
DB 0
ONE_DRIVE:
DB 0
SP_STORAGE:
DW 0
SECTOR_COUNTER:
DW 0
; Procedure: READ
; Purpose: Disk read
; Entry: [AL] = I/O driver # (base 0)
; [AH] = Verify flag (write only) 1 = verify after write
; [CX] = # of physical sectors to transfer
; [DX] = logical sector # to start with
; [DS:BX] = Transfer address
; Exit: If successful return with Carry Flag clear
; If unsuccessful, Carry Flag Set, CX = sectors remaining
; [AL] = Error Code
; 0 = Write protect (Disk writes only)
; 2 = Not ready
; 4 = Data
; 6 = Seek
; 8 = Sector not found
; 10 = write fault
; 12 = Misc. disk error
DIRECT_READ LABEL NEAR
SHL AL,1 ; Simulate DSKCHG/MAPDEV
READ_FAR PROC FAR
READ LABEL NEAR
MOV AH,02 ; Read command to [AH]
JMP SHORT RW_COMMON ; Join common code
SEC_NOT_FND:
CLC ; RET in case of seek error
MOV AL,8
RET
READ_FAR ENDP
; Procedure: WRITE
; Purpose: Disk write
; Entry: Same as READ
; Exit: Same as READ
DIRECT_WRITE LABEL NEAR
SHL AL,1 ; Simulate DSKCHG/MAPDEV
WRITE_FAR PROC FAR
WRITE LABEL NEAR
MOV CS:BYTE PTR VERIFY_FLAG,AH
MOV AH,03 ; Write command
RW_COMMON:
MOV CS:BYTE PTR IO_DRIVER,AL ; Get I/O Driver into [AL]
SHR AL,1 ; Convert I/O Driver to Floppy Drive #
JCXZ SEC_NOT_FND ; If # of sectors to transfer is zero, exit
MOV SI,DX ; Move # of first sector to transfer to [SI]
ADD SI,CX ; add # of sectors to transfer
CMP SI,641 ; Check for possible overflow
CMC ; Adjust carry so we'll exit if request is for
JC SEC_NOT_FND+1 ; more than # of sectors on dbl-sided disk
PUSH ES ; Save segment registers before saving stack,
PUSH DS ; so if we error exit out we can recover them
PUSH DS ; Set [ES] = [DS]
POP ES
PUSH CS ; Set [DS] = [CS]
POP DS
MOV WORD PTR SP_STORAGE,SP ; Now save Stack pointer
MOV BYTE PTR COMMAND_STORAGE,AH ; Save command - READ or WRITE
CMP BYTE PTR ONE_DRIVE,1 ; Single drive system ?
JNZ RW2 ; No, continue
; Only one drive, get last I/O Driver #
PUSH DS ; Save [DS]
XOR SI,SI
MOV DS,SI ; Do XCHG w/ segment = 00
MOV AH,AL ; Move I/O Driver # to [AH]
ASSUME DS:DATA
XCHG AH,BYTE PTR ONE_DRIVE_IO ; Swap I/O Driver #'s
ASSUME DS:CODE
POP DS ; Restore [DS]
CMP AL,AH ; Same I/O Driver as last disk access ?
JZ RW1 ; Yes, skip disk swap pause and prompt
PUSH DX ; Save [DX] to restore after prompt sent
ADD AL,41H ; Convert driver # to ASCII letter
MOV BYTE PTR DRIVE_LETTER,AL ; Store drive letter in msg string
MOV DX,OFFSET INSERT_MSG ; Point to msg string
CALL PRINT_STRING ; Output message
CALL FLUSH_FAR ; Clear Keyboard Buffer
MOV AH,00 ; Go wait for keystroke
INT 16H
POP DX ; Restore [DX]
RW1:
MOV AL,00 ; Force drive 0 for one drive system
RW2:
XCHG DX,AX ; Move first sector to transfer to [AX]
MOV DH,08 ; Convert logical sector to track and sector
DIV DH ; by dividing by number of sectors/track
INC AH ; Sectors are numbered 1 to 8 (Not 0 to 7)
MOV DH,0 ; Set head select byte to Head 0
TEST BYTE PTR IO_DRIVER,1 ; Is this a double-sided drive ?
JZ RW3 ; No, skip head select adjustment
SHR AL,1 ; Divide desired track by 2, carry flag is set
RCL DH,1 ; if track is odd - carry flag selects head
RW3:
XCHG AL,AH ; [AL] = Sector, [AH] = track
XCHG CX,AX ; [CX] now track/sector, [AX] # of sectors to move
MOV WORD PTR SECTOR_COUNTER,AX ; Initialize sector counter
MOV DI,ES ; Determine if DMA transfer will cross DMA page
SHL DI,1 ; latch boundary. [ES] is segment base for
SHL DI,1 ; transfer. [DI] becomes the least 16
SHL DI,1 ; significant bits of the 20 bit address
SHL DI,1 ; implied by the [ES] register
ADD DI,BX ; Add Offset base address in [BX] and the length of
ADD DI,01FFH ; one sector to determine maximum transfer address
JC RW5 ; Overflow indicates we're beyond DMA page boundary
XCHG BX,DI ; No overflow, move maximum transfer address to [BX]
SHR BH,1 ; Convert maximum transfer address to # of
MOV AH,80H ; 512 byte sectors we may transfer in [BH]
SUB AH,BH ; Most we can ever transfer is 128, so subtract
MOV BX,DI ; the maximum allowed by transfer address and
CMP AH,AL ; compare result to # of sectors to transfer
JBE RW4 ; If we're transfering less than maximum, skip
; swap
MOV AH,AL ; Transfer maximum, then
RW4:
PUSH AX ; Save # of sectors to transfer on stack
MOV AL,AH ; [AL] = # of sectors to move on CALL below
CALL RW9 ; Do sector transfers
POP AX ; [AL] = # of sectors transferred
SUB AL,AH ; Adjust [AH] for sectors just done
JZ RW8 ; If zero, we're through, exit
; If we're not through, we're on a DMA latch
RW5: ; page boundary and we need to work through
DEC AL ; a buffer. Adjust sector counter for
PUSH AX ; buffered operation and save our place
CLD ; Assure forward direction for data moves
PUSH BX ; Save present state of transfer address
PUSH ES
CMP BYTE PTR COMMAND_STORAGE,2 ; READ operation ?
JZ RW6 ; Yes, go read into BIOS buffer
MOV SI,BX ; It's a write so we need to fill buffer first
PUSH CX ; [CX] becomes a word counter
MOV CX,100H ; Prep to move one sector (512 bytes)
PUSH ES ; Use segment address in [ES] for write
POP DS ; to disk buffer
PUSH CS
POP ES
MOV DI,OFFSET MORE_INIT ; This code is overlayed for a one
MOV BX,DI ; secotr disk buffer
REPZ MOVSW ; Move sector from DTA to buffer
POP CX ; Restore [CX]
PUSH CS ; Now point [DS] into code segment for move
POP DS ; from buffer to disk
CALL RW13 ; Write one sector out to disk
POP ES ; Restore registers
POP BX
JMP SHORT RW7 ; Skip over buffered sector read code
RW6:
MOV BX,OFFSET MORE_INIT ; Read sector into buffer in BIOS
PUSH CS
POP ES
CALL RW13 ; Entrance to disk routine for one sector operation
MOV SI,BX ; Prep to move sector from buffer in BIOS
POP ES ; to requested Disk Transfer Address
POP BX
MOV DI,BX
PUSH CX
MOV CX,100H
REPZ MOVSW ; Do it
POP CX ; Restore word counter
RW7:
ADD BH,02 ; Increment Transfer Address by 512 bytes
POP AX ;
CALL RW9 ; Go do rest of sector transfers, if any
RW8:
POP DS ; Restore segment registers
POP ES
CLC ; Indicate successful operation
RET
WRITE_FAR ENDP
RW9_FAR PROC FAR ; Master READ/WRITE routine
RW9 LABEL NEAR
OR AL,AL ; [AL] = # of sectors to transfer
JZ RW18 ; If zero, we're done
MOV AH,09 ; Not done, compute # of sectors left
SUB AH,CL ; on this track [CL] = physical sector
CMP AH,AL ; Less than a track to go ?
JBE RW10 ; Yes, just do what's left of sectors
MOV AH,AL ; More than just this track, do to end of track
RW10:
PUSH AX ; Save our place
MOV AL,AH ; Pass # of tracks to do in [AL]
CALL RW14 ; Enter Do-op routine at retry counter initialization
POP AX ; Back from sector move, get back where we are
SUB AL,AH ; Adjust for what we just did
SHL AH,1 ; Multiply # of sectors transferred by 2 to get
ADD BH,AH ; affect on DTA address and adjust DTA
JMP RW9 ; Go do some more sectors, if necessary
RW11:
XCHG DI,AX ; There's been an error save disk status in [DI]
MOV AH,00 ; Prep for disk reset
INT 13H ; Do it
DEC SI ; Decrement retry counter
JZ RW12 ; If it's expired, look-up error code and exit
MOV AX,DI ; Otherwise get back error code in [AH]
CMP AH,80H ; Is it a write protect error ?
JZ RW12 ; Yes, no need to retry, just error out
POP AX ; No, get our place back for sector transfers
JMP SHORT RW15 ; and let's go try again
RW12:
PUSH CS ; Error exit
POP ES
MOV AX,DI ; Prepare to scan error code table
MOV AL,AH
MOV CX,000AH
MOV DI,OFFSET DISK_ERROR_TABLE
REPNZ SCASB ; Do it
MOV AL,[DI+9] ; Use pointer to disk error in [DI] to get DOS error code
MOV CX,WORD PTR SECTOR_COUNTER ; Pass sector counter back to DOS
MOV SP,WORD PTR SP_STORAGE ; Stack is messed up, restablish to entry condition
POP DS ; Restore entry segment condition
POP ES
STC ; Indicate error and exit
RET
RW9_FAR ENDP
RW13 PROC NEAR
MOV AL,1 ; Entry point for buffered operation
RW14: ; [AL] = one sector only
MOV SI,0005 ; Initial operation entry point, initialize the retry counter
MOV AH,BYTE PTR COMMAND_STORAGE ; Get desired command out of storage
RW15:
PUSH AX ; Retry entry point
CMP CH,40 ; Track > 40 ?
JC RW16 ; No, continue
SUB CH,40 ; Yes, adjust for second side
XOR DH,1 ; Indicate next head
RW16:
INT 13H ; Do operation in [AH]
JC RW11 ; Carry set indicates error
POP AX ; [AL] = # of sectors operated on
PUSH AX ; Re-save on stack
CMP WORD PTR COMMAND_STORAGE,0103H ; Write cmd w/ verify ?
JNZ RW17 ; No, skip verify
MOV AH,4 ; Yes, do verify [AL] = # of sectors to verify
INT 13H
JC RW11 ; Carry set indicates error
RW17:
POP AX ; Get # of sectors operated on
MOV AH,0 ; Prep for 16 bit subtract
SUB WORD PTR SECTOR_COUNTER,AX ; Adjust for sectors transferred
ADD CL,AL ; Add sectors just done to physical sector #
CMP CL,8 ; Have we finished a track ?
RW18:
JBE RW20 ; No, skip track increment and head toggle
MOV CL,1 ; Track finished, reset starting sector
TEST BYTE PTR IO_DRIVER,1 ; Double-sided disk ?
JZ RW19 ; No, skip head select
XOR DH,01 ; Double-sided disk, select other head
JNZ RW20 ; If not now head 0, skip track increment
RW19:
INC CH ; Increment track
RW20:
RET
RW13 ENDP
INSERT_MSG:
DB 13,10,'Insert diskette for drive '
DRIVE_LETTER:
DB 'A: and strike'
DB 13,10,'any key when ready',13,10,10,00
DISK_ERROR_TABLE:
DB 80H,40H,20H,10H,09,08,04,03,02,01
DOS_ERROR_CODES:
DB 02H,06,0CH,04,0CH,04,08,00,0CH,0CH
DB 1DH,3CH,00,74H,1DH,8AH,0EH,7FH,1DH,0F6H,0D1H,0B4H,00,89H
DB 0C3H,8AH,87H,0C9H,32H,3AH,06,72H,1DH,0B0H,0FFH,77H,01
DB 40H,22H,0C1H,0D0H,0D8H
DB 80H,40H,20H,10H,09,08,04,03,02,01
DB 02,06,0CH,04,0CH,04,08,00,0CH,0CH
DB ' ready',13,10,10,00
DB 80H,40H,20H,10H,09,08,04,03,02,01
DB 02,06,0CH,04,0CH,04,08,00,0CH,0CH
DB 29 DUP (0)
; Procedure: INIT
; Purpose: System initialization
; Entry: Established by bootstrap loader
; Exit: Jump to 100H in new program segment
INIT_FAR PROC FAR
INIT LABEL NEAR
PUSH SI ; Save registers on stack
PUSH DI
PUSH DS
PUSH ES
PUSH CX
PUSH AX
XOR AX,AX ; Set-up [DS] and [ES] for block move
MOV ES,AX
PUSH CS
POP DS
MOV SI,OFFSET DSKTBL ; Point to disk parameter table at end of code
MOV DI,0570H ; Point to BIOS RAM Area
MOV AX,DI ; Save pointer in [AX]
MOV CX,11 ; Prep to move 11 bytes
REPZ MOVSB ; Do it
MOV DI,0078H ; Point to INT 1EH, pointer to dsk parm table
STOSW ; Store offset address of BIOS RAM area table
XOR AX,AX ; Set segment address for interrupt to 00
STOSW
POP AX ; Restore registers off stack
POP CX
POP ES
POP DS
POP DI
POP SI
JMP MORE_INIT ; Continue init elsewhere
INIT_FAR ENDP
DSKTBL:
DB 11011111B ; SRT=D, HD UNLOAD = 0F - 1st specify byte
DB 00000010B ; HD LOAD = 1, MODE = DMA - 2nd specify byte
DB 25H ; Wait after OPN til motor off
DB 2 ; 512 bytes/sector
DB 8 ; EOT (last sector on track)
DB 2AH ; Gap length
DB 0FFH ; DTL
DB 50H ; Gap length for format
DB 0F6H ; Fill byte for format
DB 00 ; Head settle time (milliseconds)
DB 4 ; Motor start time (1/8 seconds)
DB 0E9H,0C9H,0FFH ; Un-explained bytes
DB 179H DUP (0)
CODE ENDS
END BIOS